#include <stdio.h>
#include <limits.h>
#include <windows.h>
#include <windowsx.h>

#include "osdep/keycodes.h"
#include "input/input.h"
#include "input/mouse.h"
#include "mp_msg.h"
#include "video_out.h"
#include "aspect.h"
#include "w32_common.h"
#include "mp_fifo.h"
#include "w32_gui.h"
#include "winstuff.h"
#include "resource.h"
#include "version.h"
#include "sub.h"
#include <GL/gl.h>




extern int enable_mouse_movements;

#ifndef MONITOR_DEFAULTTOPRIMARY
#define MONITOR_DEFAULTTOPRIMARY 1
#endif

static const char* classname = "MPlayer - Media player for Win32";
int vo_vm = 0;

// last non-fullscreen extends
int prev_width;
int prev_height;
int prev_x;
int prev_y;
int gl_gen;

uint32_t image_width;
uint32_t image_height;
uint32_t image_format;
uint32_t o_dwidth;
uint32_t o_dheight;

static HINSTANCE hInstance;
#define vo_window vo_w32_window
HWND vo_window = 0;
static int event_flags;
static int mon_cnt;

static HMONITOR (WINAPI* myMonitorFromWindow)(HWND, DWORD);
static BOOL (WINAPI* myGetMonitorInfo)(HMONITOR, LPMONITORINFO);
static BOOL (WINAPI* myEnumDisplayMonitors)(HDC, LPCRECT, MONITORENUMPROC, LPARAM);
static int createRenderingContext(void);

extern int force_load_font;
extern int use_cuspal;
extern int reverse_pal;
extern int spu_pal;
extern int save_volume;
extern char *sub_font_name;
extern float text_font_scale_factor;
extern int sub_pos;
extern char *filename;
extern char *dvd_device;
extern float expand;


void gl_resize(void)
{
    int width,height,top=0;
    aspect(&width,&height,A_ZOOM);
    if (!vo_keepaspect)
    {
      if ( (factor_x == 0) && (factor_y == 0) )
      {
        width = vo_screenwidth;
        height = vo_screenheight;
      }
      else
      {
        width = (int)(width*(1+factor_x))>>1<<1;
        height = (int)(height*(1+factor_y))>>1<<1;
      }
    }
    
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(rd.left, rd.top, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (gl_gen == 1)
      glOrtho (0, image_width, image_height, 0, -1.0, 1.0);
    else if (gl_gen == 2)
      glOrtho (0, 1, 1, 0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();    
} 

static uint32_t OpenGL_ManageDisplay() {
    int width,height,top=0;
    if (disable_manage) return 0;

    if(vo_fs){
        aspect(&width,&height,A_ZOOM);
        if (!vo_keepaspect)
        {
          if ( (factor_x == 0) && (factor_y == 0) )
          {
            width = vo_screenwidth;
            height = vo_screenheight;
          }
          else
          {
            width = (int)(width*(1+factor_x))>>1<<1;
            height = (int)(height*(1+factor_y))>>1<<1;
          }
        }
        rd.left=(vo_screenwidth-width)/2 + delta_x;
        if (expand >= 0)
            rd.top=(vo_screenheight-height)/2 + delta_y;
        else
            rd.top=(vo_screenheight+height*expand+1)/2 + delta_y;
        mp_input_queue_cmd(mp_input_parse_cmd("vo_glresize"));
        
        SeekbarWidth = vo_screenwidth - VOLUMEBAR_W;
        SetWindowPos(hWndVolumebar,NULL,SeekbarWidth,0,VOLUMEBAR_W,VOLUMEBAR_H,SWP_NOZORDER|SWP_NOOWNERZORDER);
        SetWindowPos(hWndSeekbar,NULL,0,0,SeekbarWidth,VOLUMEBAR_H,SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER);
		SetWindowPos(hWndControlbar,NULL,0,vo_screenheight-VOLUMEBAR_H+4,vo_screenwidth,VOLUMEBAR_H,SWP_NOZORDER|SWP_NOOWNERZORDER);
        FullHide = !auto_hide;
    }
    else {
        POINT pt;

		if (bFullView) {
        	GetClientRect(hWndGUI, &rd);
	        width = rd.right;
    	    height = rd.bottom - VOLUMEBAR_H;
		} else {
        	GetWindowRect(hWndGUI, &rd);
	        width = rd.right - rd.left;
    	    height = rd.bottom - rd.top;
		}
        
        SeekbarWidth = width - VOLUMEBAR_W;
		SetWindowPos(hWndControlbar,NULL,0,height,width,VOLUMEBAR_H,SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
        SetWindowPos(hWndVolumebar,NULL,SeekbarWidth,0,VOLUMEBAR_W,VOLUMEBAR_H,SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
        SetWindowPos(hWndSeekbar,NULL,0,0,SeekbarWidth,VOLUMEBAR_H,SWP_NOMOVE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
		PostMessage(hWndGUI, WM_SHOWCONTROLBAR, 0, 0);
        FullHide = FALSE;
       
        
        ClientToScreen(hWndGUI, &pt);
        top = pt.y;
        if (vo_keepaspect){
            int tmpheight=((float)width/window_aspect);
			tmpheight+=tmpheight&1;
            if(tmpheight > height){
                pt.x += (width - height * window_aspect) / 2;
                width = height * window_aspect;
				width += width & 1;
            }
            else
            {
                pt.y += (height - tmpheight) >> 1;
                height = tmpheight;
            }
        }
        rd.left = pt.x - xinerama_x;
        rd.top = pt.y - xinerama_y;
    }
    
    rd.right=rd.left+width;
    rd.bottom=rd.top+height;
    
    disp_top = rd.top - top;
    disp_height = rd.bottom - top - disp_top;
    
    return 0;
}

static void SetView(void)
{
	RECT rc;
#if USE_REGION
	static HRGN RgnOld = 0;
	HRGN RgnNew;
#else
	static BOOL bOldView = TRUE;

	if (bOldView == bFullView) return;
#endif

	disable_manage = 1;

#if USE_REGION
	GetWindowRect(hWndGUI, &rc);
#endif
	if (bFullView) {
#if USE_REGION
		RgnNew = CreateRectRgn(0, 0, rc.right-rc.left, rc.bottom-rc.top);
#else
		xborder = xborder0;
		yborder = yborder0;
		GetWindowRect(hWndGUI, &rc);
		AdjustWindowRect(&rc, WNDSTYLE, TRUE);
		SetWindowLong(hWndGUI, GWL_STYLE, WNDSTYLE|WS_VISIBLE);
		SetWindowPos(hWndGUI, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top+VOLUMEBAR_H,
			SWP_NOZORDER|SWP_NOOWNERZORDER);
		SetMenu(hWndGUI, hMenu);
		ShowWindow(hWndControlbar, SW_SHOW);
#endif
	}
	else {
		POINT p;

		p.x = p.y = 0;
		ClientToScreen(hWndGUI, &p);
#if USE_REGION
		p.x -= rc.left;
		p.y -= rc.top;
		GetClientRect(hWndGUI, &rc);
		RgnNew = CreateRectRgn(p.x, p.y, p.x+rc.right, p.y+rc.bottom-VOLUMEBAR_H);
#else
		xborder = yborder = 0;
		GetClientRect(hWndGUI,&rc);
		SetMenu(hWndGUI, NULL);
		ShowWindow(hWndControlbar, SW_HIDE);
		SetWindowLong(hWndGUI, GWL_STYLE, WS_POPUP|WS_VISIBLE);
		SetWindowPos(hWndGUI, NULL, p.x, p.y, rc.right, rc.bottom-VOLUMEBAR_H,
			SWP_NOZORDER|SWP_NOOWNERZORDER);
#endif
	}
#if USE_REGION
	SetWindowRgn(hWndGUI, RgnNew, TRUE);
	DeleteObject(RgnOld);
	RgnOld = RgnNew;
#else
	bOldView = bFullView;
#endif
    mp_input_queue_cmd(mp_input_parse_cmd("vo_glresize"));
	disable_manage = 0;
	OpenGL_ManageDisplay();
}

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{
    RECT r;
    POINT p;
    switch (message) {
        case WM_PAINT:
            event_flags |= VO_EVENT_EXPOSE;
            break;
    	case WM_NCPAINT:
			if (!vo_fs && !bFullView) {
				RECT rc;
    			HDC hdc;
				GetWindowRect(hWnd, &rc);
				rc.right -= rc.left;
				rc.bottom -= rc.top;
				rc.left = 0;
				rc.top = 0;
			    hdc = GetWindowDC(hWnd);
				FillRect(hdc, &rc, bIdle?bkgndbrush:colorbrush);
			    ReleaseDC(hWnd, hdc);
				return 0;
			}
			break;
		case WM_ERASEBKGND:
		{
			RECT rc;
			GetClientRect(hWnd, &rc);
			if (!vo_fs) {
				if (bIdle) {
					HDC dc1 = CreateCompatibleDC((HDC)wParam);
					HDC dc2 = CreateCompatibleDC((HDC)wParam);
					HBITMAP bmp = CreateCompatibleBitmap((HDC)wParam, rc.right, rc.bottom);
					SelectObject(dc1, bmp);
					SelectObject(dc2, hLogo);
					FillRect(dc1, &rc, bkgndbrush);
					BitBlt(dc1,	(rc.right+rc.left-logoW)>>1, (rc.bottom+rc.top-logoH)>>1,
						logoW, logoH, dc2, 0, 0, SRCCOPY);
					BitBlt((HDC)wParam, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
						dc1, rc.left, rc.top, SRCCOPY);
					DeleteObject(bmp);
					DeleteDC(dc1);
					DeleteDC(dc2);
				} else {
					FillRect((HDC)wParam, &rc, colorbrush);
				}
			} else {
				FillRect((HDC)wParam, &rc, bIdle?bkgndbrush:blackbrush);
			}
			InvalidateRect(hWndVolumebar, NULL, FALSE);
		}
        case WM_COMMAND:
	    {
	   		char tmps[MAX_PATH];
			char *filename;

			if (LOWORD(wParam) >= IDM_SUBTITLE) {
				CheckMenuRadioItem(hSubtitleMenu, IDM_SUBTITLE, IDM_SUBTITLE + 99,
					LOWORD(wParam), MF_BYCOMMAND);
				sprintf(tmps, "sub_select %d", LOWORD(wParam) - IDM_SUBTITLE - 1);
				mp_input_queue_cmd(mp_input_parse_cmd(tmps));
				return 0;
			}

		   	switch (LOWORD(wParam)) {
				case IDM_OPEN:
				case IDM_ADD: {
					char *cmdline = NULL;
					BOOL bOpen = LOWORD(wParam)==IDM_OPEN;
					int i = GetOpenFile(bOpen?"Open File(s) to Play":"Add File(s) to Playlist",
						&filename, bOpen?&cmdline:NULL);
					if (i==1)
						sprintf(tmps, "%slist \"%s\"", bOpen?"load":"add", filename);
					else if (i==2)
						sprintf(tmps, "%sfile \"%s\"", bOpen?"load":"add", filename);
					else
						break;
					mp_parse_commandline(cmdline);
					mp_input_queue_cmd(mp_input_parse_cmd(tmps));
					break;
				}
				case IDM_LOAD_SUB:
					if (GetSubFile(&filename)) {
						sprintf(tmps, "sub_load \"%s\"", filename);
						mp_input_queue_cmd(mp_input_parse_cmd(tmps));
					}
					break;
				case IDM_SWITCH_FONT:
					mp_input_queue_cmd(mp_input_parse_cmd("switch_font"));
					break;
				case IDM_SUB_FONT:
					if (GetFontFile(AlternativeFont)) {
						sub_font_name = AlternativeFont;
						force_load_font = 1;
						vo_osd_changed(OSDTYPE_SUBTITLE);
					}
					break;
				case IDM_EXIT:
				case IDM_CLOSE:
					mp_input_queue_cmd(mp_input_parse_cmd("quit"));
					break;
				case IDM_KEEP_ASPECT:
					vo_keepaspect = !vo_keepaspect;
					CheckMenuItem(hMenu, IDM_KEEP_ASPECT, vo_keepaspect?MF_CHECKED:MF_UNCHECKED);
					OpenGL_ManageDisplay();
					break;
				case IDM_FIXED_SIZE:
					fixedsize = !fixedsize;
					CheckMenuItem(hMenu, IDM_FIXED_SIZE, fixedsize?MF_CHECKED:MF_UNCHECKED);
					break;
				case IDM_CUSPAL:
					use_cuspal = !use_cuspal;
					CheckMenuItem(hMenu, IDM_CUSPAL, use_cuspal?MF_CHECKED:MF_UNCHECKED);
					break;
				case IDM_REVPAL:
					reverse_pal = !reverse_pal;
					CheckMenuItem(hMenu, IDM_REVPAL, reverse_pal?MF_CHECKED:MF_UNCHECKED);
					break;
				case IDM_HOMEPAGE:
					ShellExecute(0, NULL, "http://www.mplayerhq.hu", NULL, NULL, SW_NORMAL);
					break;
				case IDM_MANPAGE:
					filename = get_path("man_page.html");
					if (GetFileAttributes(filename) == 0xFFFFFFFF)
						ShellExecute(0, NULL, "http://www.mplayerhq.hu/DOCS/man/en/mplayer.1.html", NULL, NULL, SW_NORMAL);
					else
						ShellExecute(0, NULL, filename, NULL, NULL, SW_NORMAL);
					free(filename);
					break;
				case IDM_CMDLINE:
					MessageBox(hWnd, get_help_text(), "MPlayer", MB_OK | MB_ICONEXCLAMATION | (vo_ontop?MB_TOPMOST:0));
					break;
				case IDM_CONFIG:
				case IDM_INPUT_CONF:
				case IDM_LOG:
				{
					static char *fnames[] = {
						"mplayer.ini", "input.ini", "log.txt"
					};
					filename = get_path(fnames[LOWORD(wParam) - IDM_CONFIG]);
					ShellExecute(0, NULL, filename, NULL, NULL, SW_NORMAL);
					free(filename);
					break;
				}
				case IDM_ABOUT: {
					MSGBOXPARAMS mp;
					memset(&mp, 0, sizeof(mp));
					mp.cbSize = sizeof(mp);
					mp.hwndOwner = hWnd;
					mp.hInstance = GetModuleHandle(NULL);
					mp.lpszText = "MPlayer for Windows "VERSION"\n\nCopyright (C) 2000-2007 MPlayer Team\n\n\nMPlayer is distributed under the terms of the GNU\n General Public License Version 2. \nSourcecode is available at http://www.mplayerhq.hu";
					mp.lpszCaption = "About";
					mp.dwStyle = MB_USERICON | MB_OK | (vo_ontop?MB_TOPMOST:0);
					mp.lpszIcon = (LPSTR)IDI_APPICON;
					MessageBoxIndirect(&mp);
					break;
				}
	   		}
		   	return 0;
	    }
        case WM_DROPFILES:
	    {
			char tmps[MAX_PATH];
			int i=0, j=DragQueryFileA((HDROP)wParam, -1,  NULL, 0);
			if (j == 1) {
				char * sub_exts[] = {  "utf", "utf8", "utf-8", "idx", "sub", "srt", "smi", "rt", "txt", "ssa", "aqt", "jss", "js", "ass", NULL};
				char *tmp_fname_ext;

				DragQueryFileA((HDROP)wParam, 0, tmps, MAX_PATH-1);
				if (tmp_fname_ext = strrchr(tmps,'.')) tmp_fname_ext++;
#ifdef USE_ICONV
				for (i = (sub_cp ? 3 : 0); sub_exts[i]; i++) {
#else
				for (i = 0; sub_exts[i]; i++) {
#endif
					if (!stricmp(sub_exts[i], tmp_fname_ext)) {
						char *filename = strdup(tmps);
						sprintf(tmps, "sub_load \"%s\"", filename);
						free(filename);
						mp_input_queue_cmd(mp_input_parse_cmd(tmps));
						goto EndDrag;
					}
				}
				if (GetKeyState(VK_CONTROL) < 0)
					strcpy(tmps, "addfile \"");
				else
					strcpy(tmps, "loadfile \"");
				DragQueryFileA((HDROP)wParam, 0, tmps+strlen(tmps), MAX_PATH-strlen(tmps)-1);
				strcat(tmps, "\"");
			} else {
				FILE * fp = fopen(mplayer_pls, "w");
				while (i<j) {
					DragQueryFileA((HDROP)wParam, i++, tmps, MAX_PATH);
					fprintf(fp, "%s\n", tmps);
				}
				fclose(fp);
				if (GetKeyState(VK_CONTROL) < 0)
					sprintf(tmps, "addlist \"%s\"", mplayer_pls);
				else
					sprintf(tmps, "loadlist \"%s\"", mplayer_pls);
			}
			mp_input_queue_cmd(mp_input_parse_cmd(tmps));
EndDrag:
			DragFinish((HDROP)wParam);

			SetForegroundWindow(hWndGUI);
			return 0;
       	}
        case WM_SHOWCURSOR:
			ShowCursor(TRUE);
			return 0;
		case WM_SHOWCONTROLBAR:
            InvalidateRect(hWndControlbar, NULL, TRUE);
            return 0;
		case WM_TIMER:
            switch (wParam) {
            case TIMER_HIDE_CURSOR:
                if (!GetMenu(hWnd))
                    while(ShowCursor(FALSE)>=0);
                KillTimer(hWnd, TIMER_HIDE_CURSOR);
                break;
            case TIMER_SWITCH_VIEW:
                if (!vo_fs && WndState == SW_NORMAL) {
                    BOOL bNewView;
                    POINT p;
                    RECT r;
                    GetCursorPos(&p);
                    GetWindowRect(hWnd, &r);
#if USE_REGION
                    bNewView = p.x>=r.left && p.x<=r.right && p.y>=r.top && p.y<=r.bottom;
#else
                    bNewView = p.x>r.left && p.x<r.right && p.y>r.top && p.y<r.bottom;
#endif
                    if (bNewView != bFullView) {
                        bFullView = bNewView;
                        SetView();
                    }
                }
                break;
            }
		    return 0;
        case WM_MOUSEMOVE:
            if (enable_mouse_movements) {
                char cmd_str[40];
                snprintf(cmd_str, sizeof(cmd_str), "set_mouse_pos %i %i",
                        GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
                mp_input_queue_cmd(mp_input_parse_cmd(cmd_str));
            }
			
			static DWORD lastp=0;
			if (lastp != lParam)
			{
				lastp = lParam;
				if (vo_fs) {
					if (auto_hide) {
						ShowCursor(TRUE);
						SetTimer(hWndGUIFS, TIMER_HIDE_CURSOR, auto_hide, NULL);
					} else
					if (!bHide) {
						bHide = TRUE;
						while(ShowCursor(FALSE)>=0);
					}
				}

				if (bMoving == MOVING_SUB) {
					int diff_x = LOWORD(lastp) - old_x;
					if (diff_x > 10 || diff_x < -10) {
						if (vo_spudec) {
							((spudec_tmp_t *)vo_spudec)->scale_x *= (diff_x > 0)?1.05:0.95;
							if (GetKeyState(VK_CONTROL) >= 0) ((spudec_tmp_t *)vo_spudec)->scale_y *= (diff_x > 0)?1.05:0.95;
						}
						text_font_scale_factor += (diff_x > 0)?0.1:-0.1;
						force_load_font = 1;
						vo_osd_changed(OSDTYPE_SUBTITLE);
						old_x = LOWORD(lastp);
					}
					else
					{
						char tmps[20];
						int h = HIWORD(lastp) - disp_top;
						if (h < 0) h = 0; else
						if (h > disp_height) h = disp_height;
						if (!FullHide) sub_pos = h*100/disp_height;
						sprintf(tmps, "sub_pos %d", FullHide?(h-old_y)>>2:0);
						old_y = h;
						mp_input_queue_cmd(mp_input_parse_cmd(tmps));
					}
				} else
				if (vo_fs) {
                    if (GetMenu(hWnd)) {
                        SetMenu(hWnd, NULL);
                    }
                    else if (HIWORD(lParam) < menuHeight) {
                        SetMenu(hWnd, hMenu);
                    }

					if (ControlbarShow && HIWORD(lParam) < vo_screenheight-VOLUMEBAR_H+4) {
						ControlbarShow = FALSE;
						ShowWindow(hWndControlbar, SW_HIDE);
					} else
					if (!ControlbarShow && HIWORD(lParam) >= vo_screenheight-VOLUMEBAR_H+4) {
						if (bHide) {
							bHide = FALSE;
							ShowCursor(TRUE);
						} else
							KillTimer(hWnd, TIMER_HIDE_CURSOR);
						ControlbarShow = TRUE;
						ShowWindow(hWndControlbar, SW_SHOW);
					}
				}
#if 1
				else
				if (bMoving == MOVING_WINDOW) {
					RECT r;
					GetWindowRect(hWnd, &r);
					SetWindowPos(hWnd, NULL,
						r.left+LOWORD(lastp)-old_x,
						r.top+HIWORD(lastp)-old_y, 0,0,
						SWP_NOSIZE|SWP_NOZORDER|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
				}
#endif
			}
            break;
		case WM_NOTIFYICON:
	 	{
			switch (lParam) {
				case WM_LBUTTONDOWN:
					if (WndState == SW_SHOWMINIMIZED) {
						if (paused && systray==2) mp_input_queue_cmd(mp_input_parse_cmd("pause"));
						PostMessage(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0);
					}
					SetForegroundWindow(hWnd);
					break;
				case WM_LBUTTONDBLCLK:
					SendMessage(hWnd, WM_COMMAND, IDM_OPEN, 0);
					SetForegroundWindow(hWnd);
					break;
				case WM_RBUTTONDOWN: {
					POINT p;
					SetForegroundWindow(hWnd);
					GetCursorPos(&p);
					TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN|TPM_RIGHTBUTTON, p.x, p.y, 0, hWnd, NULL);
					break;
				}
			}
			return 0;
	 	}
        case WM_MOVE:
            p.x = 0;
            p.y = 0;
            ClientToScreen(vo_window, &p);
            vo_dx = p.x;
            vo_dy = p.y;
            break;
		case WM_SIZING:
		    if (!vo_fs && vo_keepaspect) 
			{
				LPRECT rc = (LPRECT)lParam;
				switch (wParam)
				{
					case WMSZ_LEFT:
					case WMSZ_RIGHT:
						rc->bottom = rc->top+(rc->right-rc->left-xborder)/window_aspect+yborder+0.5;
						break;
					case WMSZ_TOP:
					case WMSZ_BOTTOM:
					case WMSZ_TOPRIGHT:
					case WMSZ_BOTTOMRIGHT:
						rc->right = rc->left+(rc->bottom-rc->top-yborder)*window_aspect+xborder+0.5;
						break;
					case WMSZ_TOPLEFT:
					case WMSZ_BOTTOMLEFT:
						rc->left = rc->right-(rc->bottom-rc->top-yborder)*window_aspect-xborder+0.5;
						break;
				}
			}
			return 0;
		case WM_SIZE:
            event_flags |= VO_EVENT_RESIZE;
            GetClientRect(vo_window, &r);
            vo_dwidth = r.right;
            vo_dheight = r.bottom; 
            break;
        case WM_EXITSIZEMOVE:
            mp_input_queue_cmd(mp_input_parse_cmd("vo_glresize"));
            break; 
        case WM_WINDOWPOSCHANGING:
            return 0;
		case WM_WINDOWPOSCHANGED:
            OpenGL_ManageDisplay();        
			break;
        case WM_CLOSE:
            mplayer_put_key(KEY_CLOSE_WIN);
            break;
        case WM_SYSCOMMAND:
        {
            LRESULT ret;
			WINDOWPLACEMENT wp;
            switch (wParam) {
                case SC_SCREENSAVE:
                case SC_MONITORPOWER:
                    mp_msg(MSGT_VO, MSGL_V, "vo: win32: killing screensaver\n");
                    return 0;
                case SC_MINIMIZE:
					if (hWndP) {
						ShowWindow(hWnd, SW_MINIMIZE);
						ShowWindow(hWnd, SW_HIDE);
						if (!paused && systray==2) mp_input_queue_cmd(mp_input_parse_cmd("pause"));
					}
					break;
            }
			ret = DefWindowProc(hWnd, message, wParam, lParam);
			GetWindowPlacement(hWnd, &wp);
			WndState = wp.showCmd;
			return ret;
        }
        case WM_SYSCHAR:
		{
			if (wParam == 13) {
				mp_input_queue_cmd(mp_input_parse_cmd("vo_fullscreen"));
			} else
			if (wParam>0x30 && wParam<0x34 && !vo_fs) {
				static float scale = 1.0;

				if (wParam == 0x32) {
					scale = 1.0;
				}
				else if (wParam == 0x31 && scale > 0.25) {
					scale -= 0.25;//(scale > 1.0)?0.5:0.25;
				}
				else if (wParam == 0x33 && scale < 3.0) {
					scale += 0.25;//(scale < 1.0)?0.25:0.5;
				}
				ShowWindow(hWnd, SW_NORMAL);
				WndState = SW_NORMAL;
				SetWindowPos(hWnd, NULL, 0, 0,
					o_dwidth*scale+xborder,
					o_dheight*scale+yborder, SWP_NOMOVE);
			} else
				break;
			return 0;
		}
        case WM_KEYDOWN:
        	if ( vo_fs && (wParam >= VK_NUMPAD1) && (wParam <= VK_DECIMAL)) {
				if (GetKeyState(VK_LCONTROL) < 0) {
					switch (wParam) {
						case VK_NUMPAD1 : delta_x -= 4; delta_y += 4; break;
						case VK_NUMPAD2 : delta_y += 4; break;
						case VK_NUMPAD3 : delta_x += 4; delta_y += 4; break;
						case VK_NUMPAD4 : delta_x -= 4; break;
						case VK_NUMPAD5 : delta_x = delta_y = 0; break;
						case VK_NUMPAD6 : delta_x += 4; break;
						case VK_NUMPAD7 : delta_x -= 4; delta_y -= 4; break;
						case VK_NUMPAD8 : delta_y -= 4; break;
						case VK_NUMPAD9 : delta_x += 4; delta_y -= 4; break;
						case VK_ADD     :
						case VK_SUBTRACT:
							spu_pal = ((spu_pal+((wParam==VK_ADD)?136:120))&0xFF)-128;
							return 0;
					}
					OpenGL_ManageDisplay();
                    createRenderingContext();
					break;
				}
				else if (GetKeyState(VK_RCONTROL) < 0) {
					vo_keepaspect = 0;
					switch (wParam) {
						case VK_NUMPAD1 : factor_x = (1 + factor_x) * 0.98 - 1; factor_y = (1 + factor_y) * 0.98 - 1; break;
						case VK_NUMPAD2 : factor_y -= 0.02; break;
						case VK_NUMPAD3 : factor_x = 0; factor_y = factors[factor_index = (factor_index+1) % 3]; break;
						case VK_NUMPAD4 : factor_x -= 0.02; break;
						case VK_NUMPAD5 : factor_x = factor_y = 0; vo_keepaspect = 1; break;
						case VK_NUMPAD6 : factor_x += 0.02; break;
						case VK_NUMPAD7 : factor_x = 0; factor_y = factors[factor_index = (factor_index+2) % 3]; break;
						case VK_NUMPAD8 : factor_y += 0.02; break;
						case VK_NUMPAD9 : factor_x = (1 + factor_x) * 1.02 - 1; factor_y = (1 + factor_y) * 1.02 - 1; break;
					}
					CheckMenuItem(hMenu, IDM_KEEP_ASPECT, vo_keepaspect?MF_CHECKED:MF_UNCHECKED);
					OpenGL_ManageDisplay();
                    createRenderingContext();
					break;
				}
			}
            switch (wParam) {
                case VK_LEFT:    mplayer_put_key(KEY_LEFT);      break;
                case VK_UP:      mplayer_put_key(KEY_UP);        break;
                case VK_RIGHT:   mplayer_put_key(KEY_RIGHT);     break;
                case VK_DOWN:    mplayer_put_key(KEY_DOWN);      break;
                case VK_TAB:     mplayer_put_key(KEY_TAB);       break;
                case VK_CONTROL: mplayer_put_key(KEY_CTRL);      break;
                case VK_BACK:    mplayer_put_key(KEY_BS);        break;
                case VK_DELETE:  mplayer_put_key(KEY_DELETE);    break;
                case VK_INSERT:  mplayer_put_key(KEY_INSERT);    break;
                case VK_HOME:    mplayer_put_key(KEY_HOME);      break;
                case VK_END:     mplayer_put_key(KEY_END);       break;
                case VK_PRIOR:   mplayer_put_key(KEY_PAGE_UP);   break;
                case VK_NEXT:    mplayer_put_key(KEY_PAGE_DOWN); break;
                case VK_ESCAPE:  mplayer_put_key(KEY_ESC);       break;
                default:
			 		if (wParam >= VK_F1 && wParam <= VK_F12)
						mplayer_put_key((KEY_F+1-VK_F1)+wParam);
            }
            break;
        case WM_CHAR:
            mplayer_put_key(wParam);
            break;
        case WM_LBUTTONDOWN:
        	start_lbutton_down = GetTickCount();
			if (!bMoving) {
				int h;
				old_x = LOWORD(lParam);
				old_y = HIWORD(lParam);
				h = old_y - disp_top;
				if (get_sub_size() && (FullHide||abs(h*100/disp_height-sub_pos)<10))
					bMoving = MOVING_SUB;
				else if (!vo_fs && WndState != SW_SHOWMAXIMIZED)
				{
					bMoving = MOVING_WINDOW;
					//ReleaseCapture();
					//PostMessage(hWnd, WM_SYSCOMMAND, 0xF012, 0);
				}
			}
			break;
        case WM_LBUTTONUP:
            bMoving = MOVING_NONE;
            if (!vo_nomouse_input && GetTickCount()-start_lbutton_down<500) {
                mplayer_put_key(MOUSE_BTN0);
            }
            break;
        case WM_MBUTTONDOWN:
            if (!vo_nomouse_input)
                mplayer_put_key(MOUSE_BTN1);
            break;
        case WM_RBUTTONDOWN:
		{
			bRBDown = TRUE;
			bWheel = FALSE;
			break;
        }
        case WM_RBUTTONUP:
		{
			bRBDown = FALSE;
			if (!vo_nomouse_input && !bWheel)
				mplayer_put_key(MOUSE_BTN2);
			break;
		}
		case WM_LBUTTONDBLCLK:
		{
			if(!vo_nomouse_input)
				mplayer_put_key(MOUSE_BTN0_DBL);
			break;
		}
		case WM_MBUTTONDBLCLK:
		{
			if (!vo_nomouse_input)
				mplayer_put_key(MOUSE_BTN1_DBL);
			break;
		}
		case WM_RBUTTONDBLCLK:
		{
			if (!vo_nomouse_input)
				mplayer_put_key(MOUSE_BTN2_DBL);
			break;
		}
        case WM_MOUSEWHEEL:
		{
			int x;

			bWheel = TRUE;
			if (vo_nomouse_input)
				break;
			x = GET_WHEEL_DELTA_WPARAM(wParam);
			if (x > 0)
				mplayer_put_key(bRBDown?MOUSE_BTN8:MOUSE_BTN3);
			else
				mplayer_put_key(bRBDown?MOUSE_BTN9:MOUSE_BTN4);
			break;
		}
        case WM_XBUTTONDOWN:
		{
			if (vo_nomouse_input)
				break;
			if (HIWORD(wParam) == 1)
				mplayer_put_key(MOUSE_BTN5);
			else
				mplayer_put_key(MOUSE_BTN6);
			break;
		}
        case WM_XBUTTONDBLCLK:
		{
			if (vo_nomouse_input)
				break;
			if (HIWORD(wParam) == 1)
				mplayer_put_key(MOUSE_BTN5_DBL);
			else
				mplayer_put_key(MOUSE_BTN6_DBL);
			break;
		}
    }
    
    return DefWindowProc(hWnd, message, wParam, lParam);
}

int vo_w32_check_events(void) {
    MSG msg;
    event_flags = 0;
    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    if (WinID >= 0) {
        RECT r;
        GetClientRect(vo_window, &r);
        if (r.right != vo_dwidth || r.bottom != vo_dheight)
            event_flags |= VO_EVENT_RESIZE;
        vo_dwidth = r.right;
        vo_dheight = r.bottom;
    }
    
    return event_flags;
}

static BOOL CALLBACK mon_enum(HMONITOR hmon, HDC hdc, LPRECT r, LPARAM p) {
    // this defaults to the last screen if specified number does not exist
    xinerama_x = r->left;
    xinerama_y = r->top;
    vo_screenwidth = r->right - r->left;
    vo_screenheight = r->bottom - r->top;
    if (mon_cnt == xinerama_screen)
        return FALSE;
    mon_cnt++;
    return TRUE;
}

void w32_update_xinerama_info(void) {
    xinerama_x = xinerama_y = 0;
    if (xinerama_screen < -1) {
        int tmp;
        xinerama_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
        xinerama_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
        tmp = GetSystemMetrics(SM_CXVIRTUALSCREEN);
        if (tmp) vo_screenwidth = tmp;
        tmp = GetSystemMetrics(SM_CYVIRTUALSCREEN);
        if (tmp) vo_screenheight = tmp;
    } else if (xinerama_screen == -1 && myMonitorFromWindow && myGetMonitorInfo) {
        MONITORINFO mi;
        HMONITOR m = myMonitorFromWindow(vo_window, MONITOR_DEFAULTTOPRIMARY);
        mi.cbSize = sizeof(mi);
        myGetMonitorInfo(m, &mi);
        xinerama_x = mi.rcMonitor.left;
        xinerama_y = mi.rcMonitor.top;
        vo_screenwidth = mi.rcMonitor.right - mi.rcMonitor.left;
        vo_screenheight = mi.rcMonitor.bottom - mi.rcMonitor.top;
    } else if (xinerama_screen > 0 && myEnumDisplayMonitors) {
        mon_cnt = 0;
        myEnumDisplayMonitors(NULL, NULL, mon_enum, 0);
    }
    aspect_save_screenres(vo_screenwidth, vo_screenheight);
}

static void updateScreenProperties() {
    DEVMODE dm;
    dm.dmSize = sizeof dm;
    dm.dmDriverExtra = 0;
    dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
    if (!EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm)) {
        mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to enumerate display settings!\n");
        return;
    }

    vo_screenwidth = dm.dmPelsWidth;
    vo_screenheight = dm.dmPelsHeight;
    vo_depthonscreen = dm.dmBitsPerPel;
    w32_update_xinerama_info();
}

static void changeMode(void) {
    DEVMODE dm;
    dm.dmSize = sizeof dm;
    dm.dmDriverExtra = 0;

    dm.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
    dm.dmBitsPerPel = vo_depthonscreen;
    dm.dmPelsWidth = vo_screenwidth;
    dm.dmPelsHeight = vo_screenheight;

    if (vo_vm) {
        int bestMode = -1;
        int bestScore = INT_MAX;
        int i;
        for (i = 0; EnumDisplaySettings(0, i, &dm); ++i) {
            int score = (dm.dmPelsWidth - o_dwidth) * (dm.dmPelsHeight - o_dheight);
            if (dm.dmBitsPerPel != vo_depthonscreen) continue;
            if (dm.dmPelsWidth < o_dwidth) continue;
            if (dm.dmPelsHeight < o_dheight) continue;

            if (score < bestScore) {
                bestScore = score;
                bestMode = i;
            }
        }

        if (bestMode != -1)
            EnumDisplaySettings(0, bestMode, &dm);

    ChangeDisplaySettings(&dm, CDS_FULLSCREEN);
    }
}

static void resetMode(void) {
    if (vo_vm)
    ChangeDisplaySettings(0, 0);
}

static int createRenderingContext(void) {
    HWND layer = HWND_NOTOPMOST;
    PIXELFORMATDESCRIPTOR pfd;
    HDC vo_hdc = GetDC(vo_window);
    RECT r;
    int pf;
  if (WinID < 0) {
    int style = (!vo_fs) ?
               (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP;
    if (vo_fs || vo_ontop) layer = HWND_TOPMOST;
    if (vo_fs) {
        changeMode();
        while (ShowCursor(0) >= 0) /**/ ;
    } else {
        resetMode();		
        while (ShowCursor(1) < 0) /**/ ;
    }
    updateScreenProperties();
    //ShowWindow(vo_window, SW_HIDE);
    //SetWindowLong(vo_window, GWL_STYLE, style);
    if (vo_fs) {
        prev_width = vo_dwidth;
        prev_height = vo_dheight;
        prev_x = vo_dx;
        prev_y = vo_dy;
        vo_dwidth = vo_screenwidth;
        vo_dheight = vo_screenheight;
        vo_dx = xinerama_x;
        vo_dy = xinerama_y;
    } else {
        // make sure there are no "stale" resize events
        // that would set vo_d* to wrong values
        vo_w32_check_events();
        vo_dwidth = prev_width;
        vo_dheight = prev_height;
        vo_dx = prev_x;
        vo_dy = prev_y;
        // HACK around what probably is a windows focus bug:
        // when pressing 'f' on the console, then 'f' again to
        // return to windowed mode, any input into the video
        // window is lost forever.
        SetFocus(vo_window);
    }
    r.left = vo_dx;
    r.top = vo_dy;
    r.right = r.left + vo_dwidth;
    r.bottom = r.top + vo_dheight;
    AdjustWindowRect(&r, style, 0);
    if (!vo_fs)
      //SetWindowPos(vo_window, layer, r.left, r.top, r.right - r.left, r.bottom - r.top + VOLUMEBAR_H + menu_height, SWP_SHOWWINDOW);
      SetWindowPos(vo_window, layer,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
    else
      SetWindowPos(vo_window, layer, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_SHOWWINDOW);
  }

    memset(&pfd, 0, sizeof pfd);
    pfd.nSize = sizeof pfd;
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 24;
    pfd.iLayerType = PFD_MAIN_PLANE;
    pf = ChoosePixelFormat(vo_hdc, &pfd);
    if (!pf) {
            mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to select a valid pixel format!\n");
        ReleaseDC(vo_window, vo_hdc);
        return 0;
    }

    SetPixelFormat(vo_hdc, pf, &pfd);
    
    mp_msg(MSGT_VO, MSGL_V, "vo: win32: running at %dx%d with depth %d\n", vo_screenwidth, vo_screenheight, vo_depthonscreen);

    ReleaseDC(vo_window, vo_hdc);
    return 1;
}

int vo_w32_config(uint32_t width, uint32_t height, uint32_t flags) {
    // store original size for videomode switching
    
    static int start_mplayer = 1;
	if ( -1 == sub_pos ) {
		if (expand > 0)
			sub_pos = 75 + expand * 25;
		else if (expand)
			sub_pos = 50 - expand * 50;
		else
			sub_pos = 90;
	}
    CheckMenuItem(hMenu, IDM_KEEP_ASPECT, vo_keepaspect?MF_CHECKED:MF_UNCHECKED);
    CheckMenuItem(hMenu, IDM_FIXED_SIZE, fixedsize?MF_CHECKED:MF_UNCHECKED);
    CheckMenuItem(hMenu, IDM_REVPAL, reverse_pal?MF_CHECKED:MF_UNCHECKED);
    CheckMenuItem(hMenu, IDM_CUSPAL, use_cuspal?MF_CHECKED:MF_UNCHECKED);
    EnableMenuItem(hMenu, IDM_CUSPAL, spu_pal?MF_GRAYED:MF_ENABLED);

    
    o_dwidth = width;
    o_dheight = height;
    
    window_aspect = (float)o_dwidth / (float)o_dheight;

    prev_width = width;
    prev_height = height;
    prev_x = vo_dx;
    prev_y = vo_dy;

    vo_vm = flags & VOFLAG_MODESWITCHING;
    vo_fs = flags & VOFLAG_FULLSCREEN;
    
    vo_fs = save_vo_fs > -1? save_vo_fs:vo_fs;

    if (WndState != SW_SHOWMAXIMIZED) {
        SetWindowPos(hWndGUI,vo_ontop?HWND_TOPMOST:(vo_rootwin?HWND_BOTTOM:HWND_NOTOPMOST),
            vo_dx,vo_dy,o_dwidth+xborder,o_dheight+yborder,
            ((start_mplayer||!fixedsize)?0:(SWP_NOMOVE|SWP_NOSIZE))|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
    }
    if (vo_fs) {
        vo_dwidth = vo_screenwidth;
        vo_dheight = vo_screenheight;
        vo_dx = xinerama_x;
        vo_dy = xinerama_y;
        ShowWindow(hWndGUIFS,SW_SHOW);
        ShowWindow(hWndGUI,SW_HIDE);
        SetForegroundWindow(hWndGUIFS);
        SetParent(hWndControlbar, hWndGUIFS);
        vo_window = hWndGUIFS;
    }
    else {
        vo_dwidth = prev_width;
        vo_dheight = prev_height;        
        GetWindowRect(vo_window,&rd);
        vo_dx=( rd.right + rd.left - xborder - (int)o_dwidth ) / 2;
        vo_dy=( rd.bottom + rd.top - yborder - (int)o_dheight ) / 2;
        vo_dx += xinerama_x; /* move position to global window space */
        vo_dy += xinerama_y;
        SetForegroundWindow(hWndGUI);
        SetParent(hWndControlbar, hWndGUI);
        ShowWindow(hWndGUIFS,SW_HIDE);
        ShowWindow(hWndGUI, SW_SHOW);
        vo_window = hWndGUI;
    }
    
    start_mplayer = 0;
    if (switch_view == 2)
		SetTimer(hWndGUI, TIMER_SWITCH_VIEW, 700, NULL);
	else
		KillTimer(hWndGUI, TIMER_SWITCH_VIEW);
    return createRenderingContext();
}

int vo_w32_init(void) {
    char exedir[MAX_PATH];
    HINSTANCE user32;
    int retry_counter=0;
    if (vo_window)
        return 1;
    
    hInstance = GetModuleHandle(0);
    
    while(!hWndGUIFS && !hWndGUI && retry_counter < 10)
    {
        retry_counter++;
        Sleep(1000);
    }
    
    if (WinID >= 0)
      vo_window = WinID;
	else if (hWndGUI > 0)
	{
	  vo_window = hWndGUI;
	  SetWindowLong((HWND)vo_window, GWL_WNDPROC, (long int)WndProc);
      SetWindowLong(hWndGUIFS, GWL_WNDPROC, (long int)WndProc);   
	}  
    else {
    vo_window = CreateWindowEx(0, classname, classname,
                  vo_border ? (WS_OVERLAPPEDWINDOW | WS_SIZEBOX) : WS_POPUP,
                  CW_USEDEFAULT, 0, 100, 100, 0, 0, hInstance, 0);
    if (!vo_window) {
        mp_msg(MSGT_VO, MSGL_ERR, "vo: win32: unable to create window!\n");
        return 0;
    }
    }

    myMonitorFromWindow = NULL;
    myGetMonitorInfo = NULL;
    myEnumDisplayMonitors = NULL;
    user32 = GetModuleHandle("user32.dll");
    if (user32) {
        myMonitorFromWindow = GetProcAddress(user32, "MonitorFromWindow");
        myGetMonitorInfo = GetProcAddress(user32, "GetMonitorInfoA");
        myEnumDisplayMonitors = GetProcAddress(user32, "EnumDisplayMonitors");
    }
	
	OpenGL_ManageDisplay();
    SetForegroundWindow(vo_window);
    BringWindowToTop(vo_window);
    updateScreenProperties();

    return 1;
}

void vo_w32_fullscreen(void) {
    save_vo_fs = vo_fs = !vo_fs;
    if (vo_fs) {
        static MENUITEMINFO mi_close = {
            sizeof(MENUITEMINFO),
            MIIM_ID|MIIM_TYPE,
            MFT_RIGHTJUSTIFY|MFT_STRING,
            MFS_ENABLED,
            IDM_CLOSE,
            NULL,
            0,
            0,
            0,
            "X",
            1
        };
        ShowWindow(hWndControlbar, SW_HIDE);
        SetParent(hWndControlbar, hWndGUIFS);
        ShowWindow(hWndGUI, SW_HIDE);
        InsertMenuItem(hMenu, 0xFFFFFFFF, TRUE, &mi_close);
        ShowWindow(hWndGUIFS, SW_SHOW);
        vo_window = hWndGUIFS;
        createRenderingContext();
        OpenGL_ManageDisplay();
        BringWindowToTop(hWndGUIFS);
        ControlbarShow = FALSE;
        PostMessage(hWndGUIFS, WM_MOUSEMOVE, 0, 0x400000);
    } else {
        SetParent(hWndControlbar, hWndGUI);
        ShowWindow(hWndControlbar, SW_SHOW);
        vo_window = hWndGUI;
        createRenderingContext();
        OpenGL_ManageDisplay();
        KillTimer(hWndGUIFS, TIMER_HIDE_CURSOR);
        DeleteMenu(hMenu, IDM_CLOSE, MF_BYCOMMAND);
        if(!GetMenu(hMenu)) SetMenu(hWndGUI, hMenu);
        ShowWindow(hWndGUI, SW_SHOW);
        ShowWindow(hWndGUIFS, SW_HIDE);
        BringWindowToTop(hWndGUI);
        SetForegroundWindow(hWndGUI);
        PostMessage(hWndGUI, WM_SHOWCURSOR, 0, 0);
    }
    
}

void vo_w32_border() {
    vo_border = !vo_border;
    createRenderingContext();
}

void vo_w32_ontop( void )
{
    vo_ontop = (vo_ontop + 1) % 3;
    if (!vo_fs) {
        HWND layer = HWND_NOTOPMOST;
        if (vo_ontop) layer = HWND_TOPMOST;
        SetWindowPos(vo_window,((vo_ontop==2)?!paused:vo_ontop)?HWND_TOPMOST:HWND_NOTOPMOST,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING);
    }
}

void vo_w32_uninit() {
    mp_msg(MSGT_VO, MSGL_V, "vo: win32: uninit\n");
    resetMode();
    ShowCursor(1);
    vo_depthonscreen = 0;
    if (WinID < 0)
    DestroyWindow(vo_window);
    vo_window = 0;
    UnregisterClass(classname, 0);
}
